/*
 *  MidiParser.cpp
 *  MusicAnalyzer
 *
 *  Created by jiro on 12/03/30.
 *  Copyright 2012 jiro music. All rights reserved.
 *
 */

#include "MidiParser.h"
#include "../UtilitySystem.h"
#include "MidiMessage.h"

namespace sc
{
	MidiParser::MidiParser( ParseOption option )
	  : option( option )
	{
		switch ( option ) {
		case Option_One:
			parseFuncPtr = &MidiParser::ParseWithOptionOne;
			break;
		case Option_None:
		default:
			parseFuncPtr = & MidiParser::ParseWithoutOption;
			break;
		}
	}

	MidiParser::~MidiParser()
	{}

	bool MidiParser::ParseWithoutOption( uint8_t* midiBytesPtr, int byteCount, std::queue< const MidiMessage* >& messages )
	{
		uint8_t* ptr = midiBytesPtr;
		for ( int i = 0; i < byteCount; )
		{
			MidiMessage* message = NULL;
			MidiMessage::Type type = MidiMessage::Type( ptr[ 0 ] & (uint8_t) 0xF0 );
			int dataByteCount = -1;
			if ( MidiMessage::IsChannelMessage( type ) )
			{
				uint8_t channel = ( ptr[ 0 ] & (uint8_t) 0x0F );
				if ( type == MidiMessage::Type_ControlChange )
				{
					type = MidiMessage::GetChannelModeMessageType( ptr[ 1 ] );
				}
				dataByteCount = MidiMessage::GetDataByteCount( type );
				message = GetChannelMidiMessage( dataByteCount, type, channel, ptr + 1 );
			}
			else	//	regard as system midi message
			{
				type = MidiMessage::Type( ptr[ 0 ] );
				dataByteCount = MidiMessage::GetDataByteCount( type );
				message = GetSystemMidiMessage( dataByteCount, type, ptr + 1 );
			}
			if ( message == NULL )
			{
				return false;
			}
			messages.push( message );
			ptr += dataByteCount;
			i += dataByteCount;
		}
		return true;
	}

	bool MidiParser::ParseWithOptionOne( uint8_t* midiBytesPtr, int byteCount, std::queue< const MidiMessage* >& messages )
	{
		uint8_t* ptr = midiBytesPtr;
		for ( int i = 0; i < byteCount; )
		{
			MidiMessage* message = NULL;
			MidiMessage::Type type = MidiMessage::Type( ptr[ 0 ] << 4 );
			int dataByteCount = -1;
			if ( MidiMessage::IsChannelMessage( type ) )
			{
				uint8_t channel = ( ptr[ 1 ] & (uint8_t) 0x0F );
				if ( MidiMessage::Type_ControlChange == type )
				{
					type = MidiMessage::GetChannelModeMessageType( ptr[ 2 ] );
				}
				dataByteCount = MidiMessage::GetDataByteCount( type );
				message = GetChannelMidiMessage( dataByteCount, type, channel, ptr + 2 );
			}
			else	//	regard as system midi message
			{
				type = MidiMessage::Type( ptr[ 1 ] );
				dataByteCount = MidiMessage::GetDataByteCount( type );
				message = GetSystemMidiMessage( dataByteCount, type, ptr + 2 );
			}
			if ( message == NULL )
			{
				return false;
			}
			messages.push( message );

			const int statusByteCount = 2;
			ptr += statusByteCount + dataByteCount;
			i += statusByteCount + dataByteCount;
		}
		return true;
	}
	
	MidiMessage* MidiParser::GetChannelMidiMessage(
		int dataByteCount, MidiMessage::Type type, uint8_t channelId, uint8_t* dataBytePtr )
	{
		MidiMessage* msg = PoolAllocator< MidiMessage >::GetInstance();
		msg->SetType( type );
		msg->SetChannelId( channelId );
		switch ( dataByteCount )
		{
		case 0:
			break;
		case 1:
			msg->SetDataByte( dataBytePtr[ 0 ] );
		case 2:
			msg->SetDataByte( dataBytePtr[ 0 ], dataBytePtr[ 1 ] );
		}
		return msg;
	}

	MidiMessage* MidiParser::GetSystemMidiMessage( int dataByteCount, MidiMessage::Type type, uint8_t* dataBytePtr )
	{
		MidiMessage* msg = PoolAllocator< MidiMessage >::GetInstance();
		msg->SetType( type );
		switch ( dataByteCount ) {
		case 0:
			break;
		case 1:
			msg->SetDataByte( dataBytePtr[ 0 ] );
		case 2:
			msg->SetDataByte( dataBytePtr[ 0 ], dataBytePtr[ 1 ] );
		}
		return msg;
	}
}
